/************************************************************
 *	ProjectName:   LT2911R
 *	FileName:	   LT2911R_COMMOn.c
 *	BuildData:	   2020-05-14
 *	Version��      V1.0
 * 	Author:        xhguo
 * 	Company:	   Lontium
 ************************************************************/

/*
V8.0 20200514
1. Cread lt2911r_common.c file, this fil include all functions that common used in all inputs and ouputs.

V8.3 20200707
1. delete unused setting while no rotation in "LT2911R_VpPro".
2. delete Vp reset while no rotation, this will cause flick issue.

*/

#include "lt2911r_common.h"
#include "i2c.h"
#include "lt2911r_lvds_to_mipi.h"
#include "printf.h"

static u8 lt2911r_ddr_pi = 0;
static u32 fm_ring_clk = 0;

void LT2911R_ChipID(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x81); // register bank
	printf("\r\n LT2911R chip ID: 0x%02x, 0x%02x, 0x%02x", HDMI_ReadI2C_Byte(0x00),
	       HDMI_ReadI2C_Byte(0x01), HDMI_ReadI2C_Byte(0x02)); // 0x18
}

void LT2911R_SystemInit(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x82);
	// HDMI_WriteI2C_Byte(0x00,0x06);
	HDMI_WriteI2C_Byte(0x01, 0x13); // Sys clk select xtal

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x00, 0x00); // fm0 select ring clk
	HDMI_WriteI2C_Byte(0x10, 0x04); // fm1 select rxpll ref clk
	HDMI_WriteI2C_Byte(0x06, 0x61); // fm0 0 div
	HDMI_WriteI2C_Byte(0x07, 0xa8);
	HDMI_WriteI2C_Byte(0x16, 0x61); // fm1 1 div
	HDMI_WriteI2C_Byte(0x17, 0xa8);

	HDMI_WriteI2C_Byte(0x12, 0xc0); // fm1 irq init
	HDMI_WriteI2C_Byte(0xff, 0x85);
	HDMI_WriteI2C_Byte(0x08, 0xff); // clr0
}

/**********************************************************
 *Function: DDR initial
 * Init DDR Pll, Phy, and Check DDR module is work or not.
 *
 *
 *
 ***********************************************************/
// chip = LT2911R_D
void LT2911R_DdrInit(void)
{
#if ROTATION
	u8 pi_min, pi_max, pi_act;

	// ddr pll
	// printf("\r\n LT2911R_DdrInit: chip select LT2911R-D");

	HDMI_WriteI2C_Byte(0xff, 0x86); //
	HDMI_WriteI2C_Byte(0x00, 0x00); // fm1 from ring

	HDMI_WriteI2C_Byte(0xff, 0xf8); // 20190505
	HDMI_WriteI2C_Byte(0x2f, 0x80);

	HDMI_WriteI2C_Byte(0xff, 0x82); // 20190505
	HDMI_WriteI2C_Byte(0x7b, 0x00); // softerware pi
	HDMI_WriteI2C_Byte(0x7d, 0x00); // softerware pi
	HDMI_WriteI2C_Byte(0x79, 0x04);

	HDMI_WriteI2C_Byte(0xff, 0x82);
	// HDMI_WriteI2C_Byte(0x01,0x18);
	HDMI_WriteI2C_Byte(0x9d, 0xf4);
	HDMI_WriteI2C_Byte(0x73, 0x00);
	HDMI_WriteI2C_Byte(0x74, 0x27);
	HDMI_WriteI2C_Byte(0x75, 0x20);
	HDMI_WriteI2C_Byte(0x76, 0x80);
	HDMI_WriteI2C_Byte(0x77, 0x00);
	HDMI_WriteI2C_Byte(0xff, 0x87);
	HDMI_WriteI2C_Byte(0x3c, 0x1a); // ddr data rate: reg * 25M *2 = 800M,
	HDMI_WriteI2C_Byte(0x3d, 0x00);
	HDMI_WriteI2C_Byte(0x3e, 0x00);

	HDMI_WriteI2C_Byte(0x21, 0x00);
	HDMI_WriteI2C_Byte(0x21, 0x80); // b7:cal_en

	// pi calibration
	// HDMI_WriteI2C_Byte(0xff,0xf8);
	// HDMI_WriteI2C_Byte(0x24,0x05);
	// HDMI_WriteI2C_Byte(0x27,0x40);

	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x78, 0x03);

	// ddr phy config
	HDMI_WriteI2C_Byte(0xff, 0x82); // 0xff
	HDMI_WriteI2C_Byte(0x7e, 0x00); // rxl0~3dealy
	HDMI_WriteI2C_Byte(0x7f, 0x00); // rxl4~7dealy
	HDMI_WriteI2C_Byte(0x80, 0x00); // rxu0~3dealy
	HDMI_WriteI2C_Byte(0x81, 0x00); // rxu4~7dealy
	HDMI_WriteI2C_Byte(0x82, 0x00); // txl0~3dealy
	HDMI_WriteI2C_Byte(0x83, 0x00); // txl4~7dealy
	HDMI_WriteI2C_Byte(0x84, 0x00); // txu0~3dealy
	HDMI_WriteI2C_Byte(0x85, 0x00); // txu4~7dealy
	HDMI_WriteI2C_Byte(0x86, 0x00); // rxdqsl/u,txdqsl/u

	HDMI_WriteI2C_Byte(0x89, 0xc0);
	HDMI_WriteI2C_Byte(0x8a, 0xc0); // squelchoffset

	HDMI_WriteI2C_Byte(0x90, 0x00); // CK delay

	HDMI_WriteI2C_Byte(0x91, 0x03); // rxbyteclkswap
	HDMI_WriteI2C_Byte(0x97, 0x14); // bit2odtencontrol,bit0ckswap
	HDMI_WriteI2C_Byte(0x98, 0x11); // CMD drv strength
	HDMI_WriteI2C_Byte(0x99, 0x0f); // DQ drv strength //0x1f
	HDMI_WriteI2C_Byte(0x9a, 0x00); // pre-emphasis
	HDMI_WriteI2C_Byte(0x9b, 0x01); // odt 0x01
	HDMI_WriteI2C_Byte(0x9c, 0x2c); // 0x2c
	HDMI_WriteI2C_Byte(0x9d, 0xb4);

	HDMI_WriteI2C_Byte(0x9e, 0xff); // eq
	HDMI_WriteI2C_Byte(0x9f, 0xff);
	HDMI_WriteI2C_Byte(0xa0, 0xff);
	HDMI_WriteI2C_Byte(0xa1, 0xff);
	HDMI_WriteI2C_Byte(0xa2, 0xff);

	HDMI_WriteI2C_Byte(0xa3, 0x20); // vref 0x20
	HDMI_WriteI2C_Byte(0xff, 0x87);
	HDMI_WriteI2C_Byte(0x40, 0x00);
	HDMI_WriteI2C_Byte(0x40, 0x01);

	// ddr controller fixed
	HDMI_WriteI2C_Byte(0xFF, 0xF8);
	HDMI_WriteI2C_Byte(0x30, 0x11);
	HDMI_WriteI2C_Byte(0x2a, 0x33); // 0x03
	HDMI_WriteI2C_Byte(0x09, 0x02);
	HDMI_WriteI2C_Byte(0x0A, 0xe8);
	HDMI_WriteI2C_Byte(0x0B, 0xe8);
	HDMI_WriteI2C_Byte(0x0C, 0x30); // 0x30
	HDMI_WriteI2C_Byte(0x0D, 0x03);
	HDMI_WriteI2C_Byte(0x10, 0x33);
	HDMI_WriteI2C_Byte(0x11, 0x32);
	HDMI_WriteI2C_Byte(0x12, 0x00);
	HDMI_WriteI2C_Byte(0x13, 0x02);
	HDMI_WriteI2C_Byte(0x19, 0x10);
	HDMI_WriteI2C_Byte(0x1a, 0xc3);
	HDMI_WriteI2C_Byte(0x1e, 0xaa); // ECO U3 improve

	HDMI_WriteI2C_Byte(0x24, 0x05);
	HDMI_WriteI2C_Byte(0x24, 0x25);
	HDMI_WriteI2C_Byte(0x25, 0xc5);
	HDMI_WriteI2C_Byte(0x34, 0x80);

	HDMI_WriteI2C_Byte(0xFF, 0x81);
	HDMI_WriteI2C_Byte(0x11, 0x07);
	HDMI_WriteI2C_Byte(0x11, 0x17);
	HDMI_WriteI2C_Byte(0x11, 0x1f);

	Timer0_Delay1ms(50);
	HDMI_WriteI2C_Byte(0xFF, 0xf8);
	pi_min = HDMI_ReadI2C_Byte(0x53);
	pi_max = HDMI_ReadI2C_Byte(0x54);
	pi_act = HDMI_ReadI2C_Byte(0x56);

	if ((pi_max > pi_min) && ((pi_max - pi_min) > 10))
		printf("\r\n LT2911R_DdrInit:ddr self-check passed");
	else
		printf("\r\n LT2911R_DdrInit:ddr self-check failed");

	printf("\r\n LT2911R_DdrInit: 0x%02x, 0x%02x, 0x%02x", pi_min, pi_max, pi_act);

	// pi_act += 0x08;  //pi offset 3 steps to compensate tempeture variation.
	// pi_act |=0x80;

	//(pi_act > 4)?(pi_act = pi_act-4):(pi_act = pi_act + 0x7c);
	lt2911r_ddr_pi = pi_act | 0x80;

	fm_ring_clk = 0;

	HDMI_WriteI2C_Byte(0xff, 0x82);		  // pi clk gating
	HDMI_WriteI2C_Byte(0x7b, lt2911r_ddr_pi); // softerware pi
	HDMI_WriteI2C_Byte(0x7d, lt2911r_ddr_pi); // softerware pi
	HDMI_WriteI2C_Byte(0x79, 0x0c);

	HDMI_WriteI2C_Byte(0xff, 0xf8);
	HDMI_WriteI2C_Byte(0x19, 0x00);
	HDMI_WriteI2C_Byte(0x25, 0xc9);
	HDMI_WriteI2C_Byte(0x2a, 0x01);
	HDMI_WriteI2C_Byte(0x26, 0x94);
	HDMI_WriteI2C_Byte(0x2f, 0x10);
	HDMI_WriteI2C_Byte(0x31, 0xb0);
	HDMI_WriteI2C_Byte(0x32, 0x07);
	HDMI_WriteI2C_Byte(0x33, 0x07);
#else
	printf("\r\n LT2911R-D:ddr unwrite");
#endif
}

void LT2911R_PiAuto(void)
{
	u32 now_ring_clk_0 = 0;
	u32 now_ring_clk_1 = 0;
	u32 now_ring_clk_2 = 0;
	u32 now_ring_clk_evrg = 0;

	HDMI_WriteI2C_Byte(0xff, 0x86); //
	HDMI_WriteI2C_Byte(0x00, 0x00); // fm1 from ring

	HDMI_WriteI2C_Byte(0xff, 0x81);
	HDMI_WriteI2C_Byte(0x40, 0xff);
	Timer0_Delay1ms(10);
	HDMI_WriteI2C_Byte(0x40, 0xf7); // disable fm sys clk, to solve multible bytes issue

	HDMI_WriteI2C_Byte(0xff, 0x86);
	now_ring_clk_0 = HDMI_ReadI2C_Byte(0x08);
	if ((now_ring_clk_0 & 0x60) != 0x60) {
		return;
	}
	now_ring_clk_0 &= 0x0f;
	now_ring_clk_0 = (now_ring_clk_0 << 8) + HDMI_ReadI2C_Byte(0x09);
	now_ring_clk_0 = (now_ring_clk_0 << 8) + HDMI_ReadI2C_Byte(0x0a);
	if ((now_ring_clk_0 < 35000) || (now_ring_clk_0 > 120000)) {
		return;
	}

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0xff, 0x81);
	HDMI_WriteI2C_Byte(0x40, 0xff);
	Timer0_Delay1ms(10);
	HDMI_WriteI2C_Byte(0x40, 0xf7); // disable fm sys clk, to solve multible bytes issue

	HDMI_WriteI2C_Byte(0xff, 0x86);
	now_ring_clk_1 = HDMI_ReadI2C_Byte(0x08);
	if ((now_ring_clk_1 & 0x60) != 0x60) {
		return;
	}
	now_ring_clk_1 &= 0x0f;
	now_ring_clk_1 = (now_ring_clk_1 << 8) + HDMI_ReadI2C_Byte(0x09);
	now_ring_clk_1 = (now_ring_clk_1 << 8) + HDMI_ReadI2C_Byte(0x0a);
	if ((now_ring_clk_1 < 35000) || (now_ring_clk_1 > 120000)) {
		return;
	}

	HDMI_WriteI2C_Byte(0xff, 0x81);
	HDMI_WriteI2C_Byte(0x40, 0xff);
	Timer0_Delay1ms(10);
	HDMI_WriteI2C_Byte(0x40, 0xf7); // disable fm sys clk, to solve multible bytes issue

	HDMI_WriteI2C_Byte(0xff, 0x86);
	now_ring_clk_2 = HDMI_ReadI2C_Byte(0x08);
	if ((now_ring_clk_2 & 0x60) != 0x60) {
		return;
	}
	now_ring_clk_2 &= 0x0f;
	now_ring_clk_2 = (now_ring_clk_2 << 8) + HDMI_ReadI2C_Byte(0x09);
	now_ring_clk_2 = (now_ring_clk_2 << 8) + HDMI_ReadI2C_Byte(0x0a);
	if ((now_ring_clk_2 < 35000) || (now_ring_clk_2 > 120000)) {
		return;
	}

	now_ring_clk_evrg = (now_ring_clk_0 + now_ring_clk_1 + now_ring_clk_2) / 3;

	// printf("\r\nLT2911R_PiAuto:%d,%d,%d,%d",now_ring_clk_0,now_ring_clk_1,now_ring_clk_2,now_ring_clk_evrg);

	if (fm_ring_clk) // fm_ring_clk !=0 means it is
	{
		if (now_ring_clk_evrg >= fm_ring_clk) {

			if (((now_ring_clk_evrg - fm_ring_clk) >= 200) && ((now_ring_clk_evrg - fm_ring_clk) < 400)) {
				lt2911r_ddr_pi += 2;
				lt2911r_ddr_pi |= 0x80;
				fm_ring_clk = now_ring_clk_evrg;
				HDMI_WriteI2C_Byte(0xff, 0x82); // pi clk gating
				HDMI_WriteI2C_Byte(0x79, 0x04);
				HDMI_WriteI2C_Byte(0x7b, lt2911r_ddr_pi); // softerware pi
				HDMI_WriteI2C_Byte(0x7d, lt2911r_ddr_pi); // softerware pi
				HDMI_WriteI2C_Byte(0x79, 0x0c);

				printf("\r\n LT2911R_PiAuto: %d, + %02x", fm_ring_clk, lt2911r_ddr_pi);
			}
		}

		else {
			if (((fm_ring_clk - now_ring_clk_evrg) >= 200) && ((fm_ring_clk - now_ring_clk_evrg) < 400)) {
				lt2911r_ddr_pi &= 0x7f;
				lt2911r_ddr_pi = (lt2911r_ddr_pi >= 2) ? (lt2911r_ddr_pi - 2) : (lt2911r_ddr_pi + 0x7E);
				lt2911r_ddr_pi |= 0x80;

				fm_ring_clk = now_ring_clk_evrg;
				HDMI_WriteI2C_Byte(0xff, 0x82); // pi clk gating
				HDMI_WriteI2C_Byte(0x79, 0x04);
				HDMI_WriteI2C_Byte(0x7b, lt2911r_ddr_pi); // softerware pi
				HDMI_WriteI2C_Byte(0x7d, lt2911r_ddr_pi); // softerware pi
				HDMI_WriteI2C_Byte(0x79, 0x0c);

				printf("\r\n LT2911R_PiAuto: %d, - %02x", fm_ring_clk, lt2911r_ddr_pi);
			}
		}
	} else // fm_ring_clk = 0;
	{
		fm_ring_clk = now_ring_clk_evrg;
		printf("\r\n LT2911R_PiAuto: fm_ring_clk = 0 --> %d ", fm_ring_clk);
	}
}

/******************************************************************
* Founction: DDR 后的Video timing 重建。
* 当前DDR 前和DDR 后的Video pixel 相同，timing 根据结构体 det_video 中的timing重新产生。
* 结构体det_timing 的timing 是根据LVDS 检测到的数据，通过旋转，blank 平均等处理，得到的�
* Retimer 有2种mode:
* Normal: T-frame of input and output video should be same.
* Vsync: Allow T-frame of input and output little different.  but require vtotal stable.

*******************************************************************/
void LT2911R_Retimer(struct video_timing *video_format)
{

#if ROTATION
	// re_timing
	HDMI_WriteI2C_Byte(0xff, 0xf9);
	HDMI_WriteI2C_Byte(0x2c, 0x80); // 0x00 normal, 0x80 vsync mode

	HDMI_WriteI2C_Byte(0x3f, (u8)(video_format->vs % 256)); // vsa

	HDMI_WriteI2C_Byte(0x40, (u8)((video_format->vs + video_format->vbp) / 256)); // v_start
	HDMI_WriteI2C_Byte(0x41, (u8)((video_format->vs + video_format->vbp) % 256)); // v_start

	HDMI_WriteI2C_Byte(0x42, (u8)(video_format->vfp / 256));
	HDMI_WriteI2C_Byte(0x43, (u8)(video_format->vfp % 256)); // vfp

	HDMI_WriteI2C_Byte(0x44, (u8)(video_format->vact / 256));
	HDMI_WriteI2C_Byte(0x45, (u8)(video_format->vact % 256)); // vactive

	HDMI_WriteI2C_Byte(0x46, (u8)(video_format->vtotal / 256));
	HDMI_WriteI2C_Byte(0x47, (u8)(video_format->vtotal % 256)); // vtotal

	HDMI_WriteI2C_Byte(0x48, (u8)(video_format->hfp / 256));
	HDMI_WriteI2C_Byte(0x49, (u8)(video_format->hfp % 256)); // hfp

	HDMI_WriteI2C_Byte(0x4a, (u8)(video_format->hs / 256));
	HDMI_WriteI2C_Byte(0x4b, (u8)(video_format->hs % 256)); // hsa

	HDMI_WriteI2C_Byte(0x4c, (u8)((video_format->hs + video_format->hbp) / 256));
	HDMI_WriteI2C_Byte(0x4d, (u8)((video_format->hs + video_format->hbp) % 256)); // h_start

	HDMI_WriteI2C_Byte(0x4e, (u8)(video_format->hact / 256));
	HDMI_WriteI2C_Byte(0x4f, (u8)(video_format->hact % 256)); // hactive

	HDMI_WriteI2C_Byte(0x5a, (u8)(video_format->htotal / 256));
	HDMI_WriteI2C_Byte(0x5b, (u8)(video_format->htotal % 256)); // htotal
#endif
}
/**************************************************


***************************************************/
void LT2911R_VpPro(struct video_timing *video_format)
{
#if ROTATION
	// vid_pro
	HDMI_WriteI2C_Byte(0xff, 0xf9);
	HDMI_WriteI2C_Byte(0x0e, 0x10);
	HDMI_WriteI2C_Byte(0x10, 0x10);
	HDMI_WriteI2C_Byte(0x56, 0x5f); // 0x5f: should tune this register base on resolution
	HDMI_WriteI2C_Byte(0x57, 0x5f);
	HDMI_WriteI2C_Byte(0x51, 0x11);
	HDMI_WriteI2C_Byte(0x3e, 0x00); // 0x80, vp bypass for debug
	HDMI_WriteI2C_Byte(0x24, 0x00);
	HDMI_WriteI2C_Byte(0x25, 0x08);
	HDMI_WriteI2C_Byte(0x2d, 0x00);
	HDMI_WriteI2C_Byte(0x2e, 0x07);
	HDMI_WriteI2C_Byte(0x2f, 0xff);
	HDMI_WriteI2C_Byte(0x30, 0xff);
	HDMI_WriteI2C_Byte(0x58, 0x00);
	HDMI_WriteI2C_Byte(0x0f, 0x00);
	HDMI_WriteI2C_Byte(0x0f, 0x04);
	// HDMI_WriteI2C_Byte(0x0f,0x00);

	// vp input timing
#if ROTATION_DIRECTION
	HDMI_WriteI2C_Byte(0x00, (0x60 | (u8)(video_format->vact / 256))); // rotate right,[7:4]=4 for left, [3:0]= rx hact[11:8]
#else
	HDMI_WriteI2C_Byte(0x00, (0x40 | (u8)(video_format->vact / 256))); // rotate right,[7:4]=4 for left, [3:0]= rx hact[11:8]
#endif

	HDMI_WriteI2C_Byte(0x01, (u8)(video_format->vact % 256)); // hactive

	HDMI_WriteI2C_Byte(0x02, (u8)(video_format->hact / 256));
	HDMI_WriteI2C_Byte(0x03, (u8)(video_format->hact % 256)); // vactive

	// vp output timing
	HDMI_WriteI2C_Byte(0x04, (u8)(video_format->hact / 256));
	HDMI_WriteI2C_Byte(0x05, (u8)(video_format->hact % 256)); // hactive

	HDMI_WriteI2C_Byte(0x06, (u8)(video_format->vact / 256));
	HDMI_WriteI2C_Byte(0x07, (u8)(video_format->vact % 256)); // vactive

#else // no video rotation
	HDMI_WriteI2C_Byte(0xff, 0xf9);
	HDMI_WriteI2C_Byte(0x3e, 0x80); // 0x80, vp bypass for debug
	printf("\r\n LT2911R_VpPro: bypass");

#endif
}

void LT2911R_CscCfg(struct video_timing *video_format, u8 input_color_space, u8 output_color_space)
{
	u32 htotal, hblank, vact;
	if ((input_color_space == RGB) && (output_color_space == RGB)) {

		// RGB --> vertical YUV444 --> vertical YUV422 --> ritation --> YUV444 --> RGB
		printf("\r\n LT2911R_CscCfg: YUV422 --> RGB");
#if ROTATION
		vact = video_format->vact; // vact is before rotation, so it is video_format's hact.
		htotal = video_format->htotal;
		hblank = video_format->htotal - video_format->hact;

		// csc mode 1
		HDMI_WriteI2C_Byte(0xff, 0xf9);
		HDMI_WriteI2C_Byte(0x86, 0x0f); //[0]RGD_VPRX_CSC_R2Y_EN
		HDMI_WriteI2C_Byte(0x89, 0x01); //[0]RGD_VPRX_CSC_YC444TO422_VER_EN
#if ROTATION_DIRECTION
		HDMI_WriteI2C_Byte(0x90, 0x07);
#else
		HDMI_WriteI2C_Byte(0x90, 0x03);
#endif

		HDMI_WriteI2C_Byte(0x91, 0x0f);

		HDMI_WriteI2C_Byte(0x8a, (u8)(vact / 256));
		HDMI_WriteI2C_Byte(0x8b, (u8)(vact % 256)); // vactive

		HDMI_WriteI2C_Byte(0x8c, (u8)(htotal / 256));
		HDMI_WriteI2C_Byte(0x8d, (u8)(htotal % 256)); // htotal

		HDMI_WriteI2C_Byte(0x8e, (u8)(hblank / 256));
		HDMI_WriteI2C_Byte(0x8f, (u8)(hblank % 256)); // hblank
#endif
	}

	if ((input_color_space == YUV422) && (output_color_space == RGB)) {
		printf("\r\n LT2911R_CscCfg: YUV422 --> RGB");
#if ROTATION
		vact = video_format->vact; // vact is before rotation, so it is video_format's hact.
		htotal = video_format->htotal;
		hblank = video_format->htotal - video_format->hact;

		// csc mode 1
		HDMI_WriteI2C_Byte(0xff, 0xf9);
		HDMI_WriteI2C_Byte(0x86, 0x0c);
		HDMI_WriteI2C_Byte(0x87, 0x03);
		HDMI_WriteI2C_Byte(0x89, 0x01);

#if ROTATION_DIRECTION
		HDMI_WriteI2C_Byte(0x90, 0x07);
#else
		HDMI_WriteI2C_Byte(0x90, 0x03);
#endif

		HDMI_WriteI2C_Byte(0x91, 0x0f);

		HDMI_WriteI2C_Byte(0x8a, (u8)(vact / 256));
		HDMI_WriteI2C_Byte(0x8b, (u8)(vact % 256)); // vactive

		HDMI_WriteI2C_Byte(0x8c, (u8)(htotal / 256));
		HDMI_WriteI2C_Byte(0x8d, (u8)(htotal % 256)); // htotal

		HDMI_WriteI2C_Byte(0x8e, (u8)(hblank / 256));
		HDMI_WriteI2C_Byte(0x8f, (u8)(hblank % 256)); // hblank
#endif
	}

	else if ((input_color_space == RGB) && (output_color_space == YUV422)) {
		// to do
	} else if ((input_color_space == YUV422) && (output_color_space == YUV422)) {
		// to do
	}

	else {
	}

#if 0
//csc mode 2 YCBCR: 565
HDMI_WriteI2C_Byte(0xff,0xf9);
HDMI_WriteI2C_Byte(0x86,0x0f); //RGB-->
HDMI_WriteI2C_Byte(0x87,0x00);
HDMI_WriteI2C_Byte(0x88,0xc0);
HDMI_WriteI2C_Byte(0x89,0x00);
HDMI_WriteI2C_Byte(0x90,0x00);
HDMI_WriteI2C_Byte(0x91,0xcf);
#endif
}

void LT2911R_VpReset(void)
{
#if ROTATION
	HDMI_WriteI2C_Byte(0xff, 0x81);
	HDMI_WriteI2C_Byte(0x0e, 0x00); // MIPI TX reset
	HDMI_WriteI2C_Byte(0x11, 0x0f);
	Timer0_Delay1ms(10);
	HDMI_WriteI2C_Byte(0x11, 0x1f);
	Timer0_Delay1ms(10);
	HDMI_WriteI2C_Byte(0x11, 0x3f);
	Timer0_Delay1ms(10);
	HDMI_WriteI2C_Byte(0x11, 0xff);
	Timer0_Delay1ms(10);
	HDMI_WriteI2C_Byte(0x0e, 0xff);
	printf("\r\n LT2911R_VpReset: reset vp done");
#endif
}

/**********************************************************
Function:
读LVDS检测到的分辨率，处理后，放到Output_video 结构体中，DDR 后重建Timing，和 mipi 输出的timing， 会调用这个结构体。
处理方法：
1. H 方向，保持不变，直接变成V方向。
2. V 方向，因为Video check的 vfp,vs,vbp 的位宽是8bit的，如果大于256，就会溢出，导致检测不准，
   所以通过Vtotal- Vact 得到Vblank，再重新分配vfp, vs, bbp, 再相应的变成H 方向.
***********************************************************/
void LT2911R_AutoVideoCfg(struct video_timing *input_video_format,
			  struct video_timing *panel_video_format,
			  struct video_timing *output_video_format)
{
	// u8 sync_pol;
	u32 hfp, hs, hbp, hact, htotal, hblank;
	u32 vfp, vs, vbp, vact, vtotal, vblank;
	u8 sync_pol;

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x20, 0x00);
	Timer0_Delay1ms(200);

	sync_pol = HDMI_ReadI2C_Byte(0x70);
	vs = HDMI_ReadI2C_Byte(0x71);
	hs = HDMI_ReadI2C_Byte(0x72);
	hs = hs * 0x100 + HDMI_ReadI2C_Byte(0x73);

	vbp = HDMI_ReadI2C_Byte(0x74);
	vfp = HDMI_ReadI2C_Byte(0x75);

	hbp = HDMI_ReadI2C_Byte(0x76);
	hbp = hbp * 0x100 + HDMI_ReadI2C_Byte(0x77);

	hfp = HDMI_ReadI2C_Byte(0x78);
	hfp = hfp * 0x100 + HDMI_ReadI2C_Byte(0x79);

	vtotal = HDMI_ReadI2C_Byte(0x7A);
	vtotal = vtotal * 0x100 + HDMI_ReadI2C_Byte(0x7B);

	htotal = HDMI_ReadI2C_Byte(0x7c);
	htotal = htotal * 0x100 + HDMI_ReadI2C_Byte(0x7d);

	vact = HDMI_ReadI2C_Byte(0x7e);
	vact = vact * 0x100 + HDMI_ReadI2C_Byte(0x7f);

	hact = HDMI_ReadI2C_Byte(0x80);
	hact = hact * 0x100 + HDMI_ReadI2C_Byte(0x81);

	input_video_format->hfp = hfp;
	input_video_format->hs = hs;
	input_video_format->hbp = hbp;
	input_video_format->hact = hact;
	input_video_format->htotal = htotal;

	input_video_format->vfp = vfp;
	input_video_format->vs = vs;
	input_video_format->vbp = vbp;
	input_video_format->vact = vact;
	input_video_format->vtotal = vtotal;

	printf("\r\n\33[32m --------------------vp input video--------------------------\033[37m");
	printf("\r\n\33[32m video check: sync_pol = 0x%02x(should be 0x0f)\033[37m", sync_pol);
	printf("\r\n\33[32m video check: hfp, hs, hbp, hact, htol= %d, %d, %d, %d, %d\033[37m", hfp, hs, hbp, hact, htotal);
	printf("\r\n\33[32m video check: vfp, vs, vbp, vact, vtol= %d, %d, %d, %d, %d\033[37m", vfp, vs, vbp, vact, vtotal);

#if ROTATION

#if RETIMER // copy pane_timing, should make sure output H*V total = input H*V total;

	output_video_format->hfp = panel_video_format->hfp;
	output_video_format->hs = panel_video_format->hs;
	output_video_format->hbp = panel_video_format->hbp;
	output_video_format->hact = panel_video_format->hact;
	output_video_format->htotal = panel_video_format->htotal;

	output_video_format->vfp = panel_video_format->vfp;
	output_video_format->vs = panel_video_format->vs;
	output_video_format->vbp = panel_video_format->vbp;
	output_video_format->vact = panel_video_format->vact;
	output_video_format->vtotal = panel_video_format->vtotal;

#else // same total

	vblank = vtotal - vact;
	vfp = vblank >> 1;
	vs = vfp >> 1;
	vbp = vblank - vfp - vs;

	hblank = htotal - hact;
	hs = panel_video_format->vs;
	hbp = panel_video_format->vbp;
	hfp = hblank - hbp - hs;

	output_video_format->hfp = vfp;
	output_video_format->hs = vs;
	output_video_format->hbp = vbp;
	output_video_format->hact = vact;
	output_video_format->htotal = vtotal;

	output_video_format->vfp = hfp;
	output_video_format->vs = hs;
	output_video_format->vbp = hbp;
	output_video_format->vact = hact;
	output_video_format->vtotal = htotal;

#endif

#else // no video rotation
	output_video_format->hfp = hfp;
	output_video_format->hs = hs;
	output_video_format->hbp = hbp;
	output_video_format->hact = hact;
	output_video_format->htotal = htotal;

	output_video_format->vfp = vfp;
	output_video_format->vs = vs;
	output_video_format->vbp = vbp;
	output_video_format->vact = vact;
	output_video_format->vtotal = vtotal;
#endif
}

void LT2911R_InterruptEnable(u8 irq, bool on)
{
	u8 reg;
	if (irq == RXPLL_CLK_UNSTB) {
		// notice: fm1 is used to check clk.
		// Please confirm that 0x8610 = the clock to be checked.
		if (on) {
			HDMI_WriteI2C_Byte(0xff, 0x85);
			reg = HDMI_ReadI2C_Byte(0x08);
			reg |= 0x80;
			HDMI_WriteI2C_Byte(0x08, reg); // clr0 bit7

			reg &= 0x7f;
			HDMI_WriteI2C_Byte(0x08, reg); // clr0

			reg = HDMI_ReadI2C_Byte(0x00);
			reg |= 0x80;
			HDMI_WriteI2C_Byte(0x00, reg); // msk0 bit7

			reg &= 0x7f;
			HDMI_WriteI2C_Byte(0x00, reg); // msk0
		} else {
			HDMI_WriteI2C_Byte(0xff, 0x85);
			reg = HDMI_ReadI2C_Byte(0x08);
			reg |= 0x80;
			HDMI_WriteI2C_Byte(0x08, reg); // clr0 bit7

			reg = HDMI_ReadI2C_Byte(0x00);
			reg |= 0x80;
			HDMI_WriteI2C_Byte(0x00, reg); // msk0 bit7
		}
	} else if (irq == VID_CHK_CHG) {
		if (on) {
			HDMI_WriteI2C_Byte(0xff, 0x86);
			HDMI_WriteI2C_Byte(0x20, 0x00); // vid chk from lvds rx
			HDMI_WriteI2C_Byte(0x9b, 0x11); // htotal vtotal threshold
			HDMI_WriteI2C_Byte(0x9e, 0xff);
			HDMI_WriteI2C_Byte(0x9e, 0x7c); // enable htotal and vtotal irq

			HDMI_WriteI2C_Byte(0xff, 0x85);
			HDMI_WriteI2C_Byte(0x0b, 0xf7); // clr3
			HDMI_WriteI2C_Byte(0x03, 0xf7); // msk3
		} else {
			HDMI_WriteI2C_Byte(0xff, 0x85);
			HDMI_WriteI2C_Byte(0x0b, 0xff); // clr3
			HDMI_WriteI2C_Byte(0x03, 0xff); // msk0
		}
	} else if (irq == DDR_FIFO_ERROR) {
		if (on) {
			HDMI_WriteI2C_Byte(0xff, 0x85);
			reg = HDMI_ReadI2C_Byte(0x08);
			reg |= 0x0c;
			HDMI_WriteI2C_Byte(0x08, reg); // clr0 bit[3:2]

			reg &= 0xf3;
			HDMI_WriteI2C_Byte(0x08, reg); // clr0 bit[3:2]

			reg = HDMI_ReadI2C_Byte(0x00);
			reg |= 0x0c;
			HDMI_WriteI2C_Byte(0x00, reg); // msk0 bit[3:2]

			reg &= 0xf3;
			HDMI_WriteI2C_Byte(0x00, reg); // msk0 bit[3:2]
		} else {
			HDMI_WriteI2C_Byte(0xff, 0x85);
			reg = HDMI_ReadI2C_Byte(0x08);
			reg |= 0x0c;
			HDMI_WriteI2C_Byte(0x08, reg); // clr0 bit[3:2]

			reg = HDMI_ReadI2C_Byte(0x00);
			reg |= 0xf3;
			HDMI_WriteI2C_Byte(0x00, reg); // msk0 bit[3:2]
		}
	} else
		printf("\r\n LT2911R_InterruptEnable: unknow irq");
}

/**************************************************
Function: 中断函数，产生中断标志，等待中断处理函数处理中断
***************************************************/

/*
//TODO
void IntbInterruptFun(void) interrupt 7	//管脚中断
{
	if(PIF == 0x02)	  // 下降沿触发，只进中断一次
	{
		PIF = 0x00; //clear interrupt flag
		clr_IE0;
	}
	//g_flag_irq_task = 1;
}
*/

/**************************************************
Function: 中断处理函数
清LT2911R 中断，产生标志，等待主循环函数处理。
1. lvds clk stable 中断.
   (仅用到一个中断)
***************************************************/
void InterruptTaskHandle()
{
}

bool LT2911R_VpInputVideoStable(struct video_timing *video_format)
{
	u32 hact, vact, sync_pol;

	HDMI_WriteI2C_Byte(0xff, 0x85);
	HDMI_WriteI2C_Byte(0x0b, 0xff); // disable vid chk irq
	HDMI_WriteI2C_Byte(0x03, 0xff);

	HDMI_WriteI2C_Byte(0xff, 0x81); // lvds rx reset
	HDMI_WriteI2C_Byte(0x0c, 0xeb);
	HDMI_WriteI2C_Byte(0x0c, 0xfb);
	Timer0_Delay1ms(100);

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x20, 0x00); // Rx muxed video
	Timer0_Delay1ms(100);

	HDMI_WriteI2C_Byte(0xff, 0x81); // video check reset
	HDMI_WriteI2C_Byte(0x10, 0xbe);
	HDMI_WriteI2C_Byte(0x10, 0xfe);
	Timer0_Delay1ms(100); // can not remove this delay

	HDMI_WriteI2C_Byte(0xff, 0x86);
	sync_pol = HDMI_ReadI2C_Byte(0x70);
	vact = HDMI_ReadI2C_Byte(0x7e);
	vact = vact * 0x100 + HDMI_ReadI2C_Byte(0x7f);

	hact = HDMI_ReadI2C_Byte(0x80);
	hact = hact * 0x100 + HDMI_ReadI2C_Byte(0x81);

	printf("\r\n hact, vact, sync_pol = %d, %d, %d", hact, vact, sync_pol);

	HDMI_WriteI2C_Byte(0xff, 0x86); // enable htotal and vtotal irq
	HDMI_WriteI2C_Byte(0x9e, 0xff);
	HDMI_WriteI2C_Byte(0x9e, 0x7c);

	HDMI_WriteI2C_Byte(0xff, 0x85);
	HDMI_WriteI2C_Byte(0x0b, 0xf7); // Enable vid chk irq
	HDMI_WriteI2C_Byte(0x03, 0xf7);

	if ((video_format->hact == hact) && (video_format->vact == vact)) {

		printf("\r\n LT2911R_VpInputVideoStable: video stable");
		return 1;
	} else {
		printf("\r\n LT2911R_VpInputVideoStable: video unstable");
		return 0;
	}
}

bool LT2911R_VpOutputVideoStable(struct video_timing *video_format)
{
	u32 hfp, hs, hbp, hact, htotal;
	u32 vfp, vs, vbp, vact, vtotal;
	u8 sync_pol;

	HDMI_WriteI2C_Byte(0xff, 0x85);
	HDMI_WriteI2C_Byte(0x0b, 0xff); // Disable vid chk irq
	HDMI_WriteI2C_Byte(0x03, 0xff);

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x20, 0x14);
	Timer0_Delay1ms(100);

	HDMI_WriteI2C_Byte(0xff, 0x81); // video check reset
	HDMI_WriteI2C_Byte(0x10, 0xbe);
	HDMI_WriteI2C_Byte(0x10, 0xfe);
	Timer0_Delay1ms(100); // can not remove this delay

	HDMI_WriteI2C_Byte(0xff, 0x86);

	sync_pol = HDMI_ReadI2C_Byte(0x70);
	vs = HDMI_ReadI2C_Byte(0x71);
	hs = HDMI_ReadI2C_Byte(0x72);
	hs = hs * 0x100 + HDMI_ReadI2C_Byte(0x73);

	vbp = HDMI_ReadI2C_Byte(0x74);
	vfp = HDMI_ReadI2C_Byte(0x75);

	hbp = HDMI_ReadI2C_Byte(0x76);
	hbp = hbp * 0x100 + HDMI_ReadI2C_Byte(0x77);

	hfp = HDMI_ReadI2C_Byte(0x78);
	hfp = hfp * 0x100 + HDMI_ReadI2C_Byte(0x79);

	vtotal = HDMI_ReadI2C_Byte(0x7A);
	vtotal = vtotal * 0x100 + HDMI_ReadI2C_Byte(0x7B);

	htotal = HDMI_ReadI2C_Byte(0x7c);
	htotal = htotal * 0x100 + HDMI_ReadI2C_Byte(0x7d);

	vact = HDMI_ReadI2C_Byte(0x7e);
	vact = vact * 0x100 + HDMI_ReadI2C_Byte(0x7f);

	hact = HDMI_ReadI2C_Byte(0x80);
	hact = hact * 0x100 + HDMI_ReadI2C_Byte(0x81);

	HDMI_WriteI2C_Byte(0xff, 0x86); // Enable lvds vid chk irq
	HDMI_WriteI2C_Byte(0x20, 0x00);

	Timer0_Delay1ms(100); // can not remove this delay

	HDMI_WriteI2C_Byte(0xff, 0x86); // enable htotal and vtotal irq
	HDMI_WriteI2C_Byte(0x9e, 0xff);
	HDMI_WriteI2C_Byte(0x9e, 0x7c);

	HDMI_WriteI2C_Byte(0xff, 0x85);
	HDMI_WriteI2C_Byte(0x0b, 0xf7);
	HDMI_WriteI2C_Byte(0x03, 0xf7);

	printf("\r\nvideo_format->hact %d, hact %d\r\nvideo_format->vact %d, vact %d",
	       video_format->hact, hact, video_format->vact, vact);

	if ((video_format->hact == hact) && (video_format->vact == vact)) {
		printf("\r\n LT2911R_VpOutputVideoStable: video stable");
		printf("\r\n\33[32m --------------------vp output video-------------------------\033[37m");
		printf("\r\n\33[32m video check: sync_pol = 0x%02x\033[37m", sync_pol);
		printf("\r\n\33[32m video check: hfp, hs, hbp, hact, htol= %d, %d, %d, %d, %d\033[37m", hfp, hs, hbp, hact, htotal);
		printf("\r\n\33[32m video check: vfp, vs, vbp, vact, vtol= %d, %d, %d, %d, %d\033[37m", vfp, vs, vbp, vact, vtotal);
		return 1;
	} else {
		printf("\r\n LT2911R_VpOutputVideoStable: video unstable");
		return 0;
	}
}

bool LT2911R_DdrFifoStatusChecked(void)
{

	u8 reg_8510, reg_8518;
	HDMI_WriteI2C_Byte(0xff, 0x85);
	reg_8510 = HDMI_ReadI2C_Byte(0x10);
	reg_8518 = HDMI_ReadI2C_Byte(0x18);

	if (reg_8510 & 0x0c) {
		printf("\r\n main loop ddr check: DDR fifo empty or full: 0x8510 =  0x%02x", reg_8510);
		HDMI_WriteI2C_Byte(0x08, 0xbf); // clr irq flag
		HDMI_WriteI2C_Byte(0x08, 0xb3);
		return 1;
	}

	if (reg_8518 & 0x0c) {
		printf("\r\n main loop ddr check: DDR fifo empty or full: 0x8518 =  0x%02x", reg_8518);
		return 1;
	}

	else
		return 0;
}

//////////////////////////////debug end////////////////////////////////////////////

void LT2911R_DDR_GPIO_Debug(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x63, 0xff);
	HDMI_WriteI2C_Byte(0xff, 0x85);
	HDMI_WriteI2C_Byte(0x81, 0x01);
	HDMI_WriteI2C_Byte(0x82, 0x23);
	HDMI_WriteI2C_Byte(0x83, 0x45);
	HDMI_WriteI2C_Byte(0x84, 0x67);
	HDMI_WriteI2C_Byte(0x85, 0x55);
	HDMI_WriteI2C_Byte(0x86, 0x55);
	HDMI_WriteI2C_Byte(0x87, 0xff);
	HDMI_WriteI2C_Byte(0x80, 0x08);
	HDMI_WriteI2C_Byte(0xff, 0xf8);
	HDMI_WriteI2C_Byte(0x3f, 0x16);
}

/******************************************************************
 *Founction: 测试 Pattern	输出。
 *根据结构体Video 中的timing，设置Video 和 pixel clk。
 *******************************************************************/
void LT2911R_Patten(struct video_timing *video_format)
{
#if (VIDEO_PATTERN)
	u32 pclk_khz;
	u8 dessc_pll_post_div = 0;
	u32 pcr_m, pcr_k;

	pclk_khz = video_format->pclk_khz;

	HDMI_WriteI2C_Byte(0xff, 0xf9);
	HDMI_WriteI2C_Byte(0x3e, 0x80);

	HDMI_WriteI2C_Byte(0xff, 0x85);
	HDMI_WriteI2C_Byte(0x88, 0xd0);

	HDMI_WriteI2C_Byte(0xa1, 0x74);
	HDMI_WriteI2C_Byte(0xa2, 0xff);

	HDMI_WriteI2C_Byte(0xa3, (u8)((video_format->hs + video_format->hbp) / 256));
	HDMI_WriteI2C_Byte(0xa4, (u8)((video_format->hs + video_format->hbp) % 256)); // h_start

	HDMI_WriteI2C_Byte(0xa5, (u8)((video_format->vs + video_format->vbp) % 256)); // v_start

	HDMI_WriteI2C_Byte(0xa6, (u8)(video_format->hact / 256));
	HDMI_WriteI2C_Byte(0xa7, (u8)(video_format->hact % 256)); // hactive

	HDMI_WriteI2C_Byte(0xa8, (u8)(video_format->vact / 256));
	HDMI_WriteI2C_Byte(0xa9, (u8)(video_format->vact % 256)); // vactive

	HDMI_WriteI2C_Byte(0xaa, (u8)(video_format->htotal / 256));
	HDMI_WriteI2C_Byte(0xab, (u8)(video_format->htotal % 256)); // htotal

	HDMI_WriteI2C_Byte(0xac, (u8)(video_format->vtotal / 256));
	HDMI_WriteI2C_Byte(0xad, (u8)(video_format->vtotal % 256)); // vtotal

	HDMI_WriteI2C_Byte(0xae, (u8)(video_format->hs / 256));
	HDMI_WriteI2C_Byte(0xaf, (u8)(video_format->hs % 256)); // hsa

	HDMI_WriteI2C_Byte(0xb0, (u8)(video_format->vs % 256)); // vsa

	// dessc pll to generate pixel clk
	HDMI_WriteI2C_Byte(0xff, 0x82); // dessc pll
	HDMI_WriteI2C_Byte(0x2d, 0x48); // pll ref select xtal

	if (pclk_khz < 44000) {
		HDMI_WriteI2C_Byte(0x35, 0x83);
		dessc_pll_post_div = 16;
	}

	else if (pclk_khz < 88000) {
		HDMI_WriteI2C_Byte(0x35, 0x82);
		dessc_pll_post_div = 8;
	}

	else if (pclk_khz < 176000) {
		HDMI_WriteI2C_Byte(0x35, 0x81);
		dessc_pll_post_div = 4;
	}

	else if (pclk_khz < 352000) {
		HDMI_WriteI2C_Byte(0x35, 0x80);
		dessc_pll_post_div = 0;
	}

	pcr_m = (pclk_khz * dessc_pll_post_div) / 25;
	pcr_k = pcr_m % 1000;
	pcr_m = pcr_m / 1000;

	pcr_k <<= 14;

	// pixel clk
	HDMI_WriteI2C_Byte(0xff, 0xd0); // pcr
	HDMI_WriteI2C_Byte(0x2d, 0x7f);
	HDMI_WriteI2C_Byte(0x31, 0x00);

	HDMI_WriteI2C_Byte(0x26, 0x80 | ((u8)pcr_m));
	HDMI_WriteI2C_Byte(0x27, (u8)((pcr_k >> 16) & 0xff)); // K
	HDMI_WriteI2C_Byte(0x28, (u8)((pcr_k >> 8) & 0xff));  // K
	HDMI_WriteI2C_Byte(0x29, (u8)(pcr_k & 0xff));	      // K
#endif
}
